<template>
  <div class="gj-loadmore">
    <div class="gj-loadmore-content" :class="{ 'is-dropped': topDropped }" :style="{ 'transform': 'translate3d(0, ' + translate + 'px, 0)' }">
      <slot name="top">
        <div class="gj-loadmore-top" v-if="topMethod">
          <div v-show="topStatus === 'loading'"><img src="../../assets/img/loading.gif" style="width:40px;height:40px;"/></div>
          <div v-show="topStatus === 'pull'||topStatus === 'drop'"><img src="../../assets/img/niutou@2x.png" style="width:40px;height:40px;"/></div>
          <div class="w-loadmore-text">{{ topText }}</div>
        </div>
      </slot>
      <slot></slot>
      <slot name="bottom" >
        <div class="gj-loadmore-bottom" v-if="bottomMethod && showBottom" >
          <div v-show="bottomStatus === 'loading'"><img src="../../assets/img/loading.gif" class="gj-loading-gif"/></div>
          <div v-show="bottomStatus === 'pull'||bottomStatus === 'done'"><img src="../../assets/img/niutou@2x.png" class="gj-pull"/></div>
          <span class="w-loadmore-text">{{ bottomText }}</span>
        </div>
      </slot>
    </div>
  </div>
</template>

<style lang="less">
.gj-loadmore {
  overflow: hidden;
}

.gj-loadmore-content.is-dropped {
      -webkit-transition: .2s;
      transition: .2s;
  }

.gj-loadmore-top{
  text-align: center;
  padding-bottom: 5px;
  height: 60px;
  /*line-height: 50px;*/
}
.gj-loadmore-bottom{
  text-align: center;
  margin-top: 10px;
}

.gj-loadmore-top {
  margin-top: -65px;
}

.gj-loadmore-bottom {
  /*margin-bottom: -60px;*/
}

.gj-loadmore-text {
  vertical-align: middle;
}

.w-loadmore-text{
  color:#999999
}

.gj-loading-gif, .gj-pull {
  width: 40px;
  height: 40px;
  @media screen and (max-width: 330px) {
    width: 30px!important;
    height: 30px!important;
  }
}

.w-loadmore-text {
  @media screen and (max-width: 330px) {
    font-size: 9px!important;/*no*/
  }
}
</style>

<script type="text/babel">
  export default {
    name: 'gj-loadmore',
    props: {
      maxDistance: {
        type: Number,
        default: 0
      },
      autoFill: {
        type: Boolean,
        default: true
      },
      distanceIndex: {
        type: Number,
        default: 2
      },
      topPullText: {
        type: String,
        default: '下拉刷新'
      },
      topDropText: {
        type: String,
        default: '释放更新'
      },
      topLoadingText: {
        type: String,
        default: '加载中...'
      },
      topDistance: {
        type: Number,
        default: 80
      },
      topMethod: {
        type: Function
      },
      bottomPullText: {
        type: String,
        default: '上拉加载更多'
      },
      bottomLoadingText: {
        type: String,
        default: '加载中...'
      },
      bottomAllLoadedText: {
        type: String,
        default: '已全部加载'
      },
      bottomDistance: {
        type: Number,
        default: 80
      },
      bottomMethod: {
        type: Function
      },
      allowLoad: {
        type: Boolean,
        default: true
      },
      showBottom: {
        type: Boolean,
        default: true
      }
    },

    data () {
      return {
        translate: 0,
        scrollEventTarget: null,
        containerFilled: false,
        topText: '',
        topDropped: false,
        bottomText: '',
        // bottomReached: false,
        direction: '',
        startY: 0,
        startScrollTop: 0,
        currentY: 0,
        topStatus: '',
        bottomStatus: '',
        bottomAllLoaded: false,
        marginBottom: '60px',
        judge: 'block'
      }
    },

    watch: {
      topStatus (val) {
        this.$emit('top-status-change', val)
        switch (val) {
          case 'pull':
            this.topText = this.topPullText
            break
          case 'drop':
            this.topText = this.topDropText
            break
          case 'loading':
            this.topText = this.topLoadingText
            break
        }
      },
      bottomStatus (val) {
        this.$emit('bottom-status-change', val)

        switch (val) {
          case 'pull':
            this.bottomText = this.bottomPullText
            break
          case 'done':
            this.bottomText = this.bottomAllLoadedText
            break
          case 'loading':
            this.bottomText = this.bottomLoadingText
            break
        }
      }
    },

    methods: {
      onTopLoaded () {
        this.translate = 0
        setTimeout(() => {
          this.topStatus = 'pull'
        }, 200)
      },

      onBottomLoaded () {
        this.bottomStatus = 'pull'

        if (!this.bottomAllLoaded && !this.containerFilled) {
          this.fillContainer()
        }
      },

      onBottomAllLoaded () {
        this.bottomStatus = 'done'
        this.bottomAllLoaded = true
        this.bottomText = this.bottomAllLoadedText
      },

      onBottomError (text) { // 错误或数据为空时可调此方法用来隐藏底部牛头
        this.bottomStatus = 'error'
        this.bottomAllLoaded = true
        this.bottomText = text || ''
      },

      reset () {
        this.$nextTick(() => {
          this.bottomAllLoaded = false
          this.init()
        })
      },

      getScrollEventTarget (element) {
        let currentNode = element
        while (currentNode && currentNode.tagName !== 'HTML' &&
          currentNode.tagName !== 'BODY' && currentNode.nodeType === 1) {
          let overflowY = document.defaultView.getComputedStyle(currentNode).overflowY
          if (overflowY === 'scroll' || overflowY === 'auto') {
            return currentNode
          }
          currentNode = currentNode.parentNode
        }
        return window
      },

      getScrollTop (element) {
        if (element === window) {
          return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop)
        } else {
          return element.scrollTop
        }
      },

      bindTouchEvents () {
        this.scrollEventTarget.addEventListener('touchstart', this.handleTouchStart)
        this.scrollEventTarget.addEventListener('touchmove', this.handleTouchMove)
        this.scrollEventTarget.addEventListener('touchend', this.handleTouchEnd)
        this.scrollEventTarget.addEventListener('scroll', this.handleScroll)
      },

      init () {
        this.topStatus = 'pull'
        this.bottomStatus = 'pull'
        this.topText = this.topPullText
        this.scrollEventTarget = this.getScrollEventTarget(this.$el)

        if (typeof this.bottomMethod === 'function') {
          this.fillContainer()
          this.bindTouchEvents()
        }
        if (typeof this.topMethod === 'function') {
          this.bindTouchEvents()
        }
      },

      fillContainer () {
        if (this.autoFill) {
          this.$nextTick(() => {
            if (this.scrollEventTarget === window) {
              this.containerFilled = this.$el.getBoundingClientRect().bottom - 50 >=
                document.documentElement.getBoundingClientRect().bottom
            } else {
              this.containerFilled = this.$el.getBoundingClientRect().bottom - 50 >=
                this.scrollEventTarget.getBoundingClientRect().bottom
            }
            if (!this.containerFilled) {
              this.bottomStatus = 'loading'
              this.bottomMethod()
            }
          })
        }
      },

      checkBottomReached () {
        if (this.scrollEventTarget === window) {
          return document.body.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight
        } else {
          return this.$el.getBoundingClientRect().bottom <= this.scrollEventTarget.getBoundingClientRect().bottom + 1
        }
      },

      handleTouchStart (event) {
        this.startY = event.touches[0].clientY
        this.startScrollTop = this.getScrollTop(this.scrollEventTarget)

        if (this.topStatus !== 'loading') {
          this.topStatus = 'pull'
          this.topDropped = false
        }
      },

      handleTouchMove (event) {
        if (this.startY < this.$el.getBoundingClientRect().top && this.startY > this.$el.getBoundingClientRect().bottom) {
          return
        }

        this.currentY = event.touches[0].clientY
        let distance = (this.currentY - this.startY) / this.distanceIndex
        this.direction = distance > 0 ? 'down' : 'up'
        if (typeof this.topMethod === 'function' && this.direction === 'down' &&
          this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== 'loading') {
          event.preventDefault()
          event.stopPropagation()
          if (this.maxDistance > 0) {
            this.translate = distance <= this.maxDistance ? distance - this.startScrollTop : this.translate
          } else {
            this.translate = distance - this.startScrollTop
          }
          if (this.translate < 0) {
            this.translate = 0
          }
          this.topStatus = this.translate >= this.topDistance ? 'drop' : 'pull'
          this.$emit('translate-change', this.translate)
        }
      },

      handleTouchEnd () {
        if (this.direction === 'down' && this.getScrollTop(this.scrollEventTarget) === 0 && this.translate > 0) {
          this.topDropped = true
          if (this.topStatus === 'drop') {
            this.translate = '60'
            this.topStatus = 'loading'
            this.topMethod()
          } else {
            this.translate = '0'
            this.topStatus = 'pull'
          }
        }
        this.$emit('translate-change', this.translate)
        this.direction = ''
      },

      handleScroll (event) {
        var vm = this
        if (!vm.allowLoad) return
        this.$nextTick(() => {
          var bottomReached = vm.checkBottomReached()
          if (typeof this.bottomMethod === 'function' && this.bottomStatus !== 'loading' && !this.bottomAllLoaded && bottomReached) {
            event.preventDefault()
            event.stopPropagation()
            this.bottomStatus = 'loading'
            this.bottomMethod()
          }
        })
      }
    },

    mounted () {
      this.init()
    },

    beforeDestroy () {
      this.scrollEventTarget.removeEventListener('touchstart', this.handleTouchStart)
      this.scrollEventTarget.removeEventListener('touchmove', this.handleTouchMove)
      this.scrollEventTarget.removeEventListener('touchend', this.handleTouchEnd)
      this.scrollEventTarget.removeEventListener('scroll', this.handleScroll)
    }
  }
</script>
